package demo3;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class StackAndQueue {
    public ArrayForStack makeUpArrayForStack() {
        return new ArrayForStack();
    }

    public MyQueue makeUpMyQueue() {
        return new MyQueue(new Stack(), new Stack());
    }

    public MyQueueImprove makeUpMyQueueImprove() {
        return new MyQueueImprove(new Stack(), new Stack());
    }

    public Stack makeUpStack() {
        return new Stack();
    }

    public SortedStack makeUpSortedStack(Stack firstStack) {
        return new SortedStack(firstStack, new Stack());
    }

    public SetOfStacks makeUpSetOfStacks() {
        return new SetOfStacks();
    }
    public AniminalRecycle makeUpAniminalRecycle(){
        return new AniminalRecycle();
    }

    @Setter
    @Getter
    public class ArrayForStack {
        private int first;
        private int second;
        private int third;
        private int forth;
        private int length;
        private int[] arrays;
        private int maxLength = 20;

        // push a => which is it push ?
        public void threeInOneArrayForStack() {
            // 1   2   3   4
            // 0   10   20   30
            // [)
            // length =
            int[] arrays = new int[30];
            this.arrays = arrays;

            int length = arrays.length;
            int partCount = (int) (length / 3);
            int lastest = length - partCount * 2;

            int first = 0;
            int second = partCount - 1;
            int third = partCount * 2 - 1;
            int forth = length;


        }

        public ArrayForStack push(int i) {
            if (length == maxLength) throw new RuntimeException("length=maxLength ,not enough room to push");
            arrays[length] = i;
            length = length + 1;
            return this;
        }

        public int pop() {
            if (length == 0) throw new RuntimeException("length=0 ,no element");
            length = length - 1;
            return arrays[length];

        }

        public int peek() {
            if (length == 0) throw new RuntimeException("length=0 ,no element");
            return arrays[length - 1];
        }

        public boolean isEmpty() {
            return length == 0;
        }

    }

    @Getter
    @Setter
    public class SetOfStacks {
        private Stack[] stacks = new Stack[100];
        int size = 0;

        public int peek() {
            return stacks[size].peek();
        }

        /**
         *
         */
        public int popAt(int index) {
            // if current index stack is nothing ,can forward to (index-1) stack to get , or (index+1) , just you like .
            return 0;
        }

        public int pop() {
            // when one stacks is nothing ,need to find other stacks. , in order asc
            int popdata = 0;
            try {
                popdata = stacks[size].pop();
            } catch (RuntimeException ex) {
                size--;
                popdata = stacks[size].pop();
            }
            return popdata;

        }

        public SetOfStacks push(int data) {

            stacks[size] = stacks[size] == null ? new Stack() : stacks[size];

            try {
                stacks[size].push(data);
            } catch (RuntimeException ex) {
                size++;
                stacks[size] = new Stack();
                stacks[size].push(data);
            }
            return this;
        }

    }

    @Setter
    @Getter
    public class Stack {

        private int maxSize = 10;
        private int currentSize = 0;
        private Node lastest;

        // need to supply basic feature,
        public class Node {
            private Node next;
            private int data;
            private int min;

            public Node() {
            }

            public Node(int data) {
                this.data = data;
            }
        }

        public int min() {
            return lastest.min;
        }

        public int popAt(int destData) {
            //  10 -> 0
            if (currentSize-- == 0) {
                throw new RuntimeException("length=0 ,no element");
            }
            int data = lastest.data;
            lastest = lastest.next;
            // recompute the min val , subStack
            //
            return data;
        }

        public int pop() {
            //  10 -> 0
            if (currentSize-- == 0) {
                throw new RuntimeException("length=0 ,no element");
            }
            int data = lastest.data;
            lastest = lastest.next;
            // recompute the min val , subStack
            //
            return data;
        }

        public int peek() {
            return lastest.data;
        }
// 10 element

        /**
         * use linklist for stack ,make the sort of point is desc,  very well!
         * 2<-1<-3
         * 1 la
         * 2.   la
         * 3.      la
         * min 2  1  1
         */
        //
        public Stack push(int data) {
            if (currentSize++ == maxSize) {// 0->2
                throw new RuntimeException("length=maxLength ,not enough room to push");
            }
            Node node = new Node(data);

            node.next = lastest;
            if (lastest == null) {
                node.min = data;
            } else {
                node.min = lastest.min < data ? lastest.min : data;
            }


            lastest = node;

            return this;
        }

        public boolean isEmpty() {
            return lastest == null;
        }
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @Setter
    public class MyQueueImprove {
        private Stack firstStack;
        private Stack secondStacks;

        public MyQueueImprove add(int data) {
            secondStacks.push(data);
            return this;
        }

        public int peek() {
            stackSwapData(firstStack, secondStacks);
            return firstStack.peek();
        }

        public int remove() {
            stackSwapData(firstStack, secondStacks);
            return firstStack.pop();
        }// pop order is => 1,2,3

        public void stackSwapData(Stack firstStack, Stack secondStacks) {
            if (firstStack.isEmpty()) {
                while (!secondStacks.isEmpty()) {
                    firstStack.push(secondStacks.pop());
                    //                 // pop order is => 3,2,1
                }
            }

        }

    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @Setter
    public class MyQueue {
        private Stack firstStack;
        private Stack secondStacks;

        public MyQueue add(int data) {
            firstStack.push(data);
            return this;
        }

        public int remove() {
            stackSwapData(firstStack, secondStacks); // A->B
            int data = secondStacks.pop();
            stackSwapData(secondStacks, firstStack);// B->A
            return data;
        }// pop order is => 1,2,3

        public void stackSwapData(Stack firstStack, Stack secondStacks) {

            while (!firstStack.isEmpty()) {
                secondStacks.push(firstStack.pop());
                //                 // pop order is => 3,2,1
            }
        }

    }

    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public class SortedStack {
        private Stack stack;
        private Stack aideStack;
        // get every subStack min value , from the

        /**
         * 9 <- 1 <- 2 <- 3
         * 1 la
         * 2.   la
         * 3.      la
         * min 2  1  1
         */
        public Stack makeUpStack() {
            return null;
        }

        // first except for top node,
        public void sortLoop() {
            Stack.Node lastest = stack.getLastest();
            //  the lastest  is top forever.
            while (!stack.isEmpty()) {
                sort(lastest);
                // back to the lastest
                lastest = stack.getLastest();
            }
        }
        //O(n^2)
        //n^2
        // 1+2+3+4+..n = n+(n-1)+(n-2)+..+1 ~ n^2
        //
        public void sort(Stack.Node lastest) {
            Stack.Node maxNode = null;
            Stack.Node maxNodePrev = null;
            Stack.Node maxNodePrevTmp = null;

            Stack.Node lastestTmp = lastest;
            int max = lastest.data;
            maxNode = lastest;
            while (lastest != null) {
                if (lastest.data >= max) {
                    max = lastest.data;
                    maxNode = lastest;
                    maxNodePrevTmp=maxNodePrev;
                }
                maxNodePrev=lastest;
                lastest = lastest.next;
            }

            if (maxNode == lastestTmp) {
                stack.pop();
            } else {
                // remove the maxNode.next,
                if (maxNodePrevTmp.next.next != null) {
                    maxNodePrevTmp.next = maxNodePrevTmp.next.next;
                } else {
                    maxNodePrevTmp.next = null;
                }
            }
            /**
             * insert new Stack .
             */
            aideStack.push(max);

        }

    }

    @NoArgsConstructor
    @Getter
    @Setter
    @AllArgsConstructor
    public static class DogAndCat{
        private boolean isDog=true;
        private int number;
    }
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public class AniminalRecycle{
        /**   the order is 先进先出
         * 1. dog
         * 2. cat
         *
         */
        private int dogCount=0;
        private int catCount=0;

        ThreadLocalRandom current =null;
        {
            current = ThreadLocalRandom.current();
        }
        private LinkedList<DogAndCat> catLinkList=new LinkedList<>();
        private LinkedList<DogAndCat> dogLinkList=new LinkedList<>();
        public void enqueue(DogAndCat dogAndCat){
          if(dogAndCat.isDog){
              dogCount++;
              dogLinkList.addLast(dogAndCat);
          }else{
              catCount++;
              catLinkList.addLast(dogAndCat);
          }
        }
        public DogAndCat dequeueAny(){
            // form any linklist kick out
            int i = current.nextInt(2);
            if (i==0){
                dogCount--;
                return dogLinkList.pollFirst();
            }else if (i==1){
                catCount--;
                return catLinkList.pollFirst();
            }else{
                throw new RuntimeException("it's impossiable!!!");
            }
        }

        public DogAndCat dequeueDog(){
            dogCount--;
            return dogLinkList.pollFirst();
        }
        public DogAndCat dequeueCat(){
            catCount--;
            return catLinkList.pollFirst();
        }
    }
}
